
package controlP5;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.net.URLEncoder;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.List;

import processing.core.PVector;

public class CP {

	/**
	 * borrowed from http://www.javapractices.com/Topic96.cjp
	 * 
	 * 
	 * @param aURLFragment String
	 * @return String
	 */
	static public String forURL(String aURLFragment) {
		String result = null;
		try {
			result = URLEncoder.encode(aURLFragment, "UTF-8");
		} catch (UnsupportedEncodingException ex) {
			throw new RuntimeException("UTF-8 not supported", ex);
		}
		return result;
	}


	/**
	 * borrowed from http://www.javapractices.com/Topic96.cjp
	 * 
	 * @param aTagFragment String
	 * @return String
	 */
	static public String forHTMLTag(String aTagFragment) {
		final StringBuffer result = new StringBuffer();

		final StringCharacterIterator iterator = new StringCharacterIterator(aTagFragment);
		char character = iterator.current();
		while (character != CharacterIterator.DONE) {
			if (character == '<') {
				result.append("&lt;");
			}
			else if (character == '>') {
				result.append("&gt;");
			}
			else if (character == '\"') {
				result.append("&quot;");
			}
			else if (character == '\'') {
				result.append("&#039;");
			}
			else if (character == '\\') {
				result.append("&#092;");
			}
			else if (character == '&') {
				result.append("&amp;");
			}
			else {
				// the char is not a special one
				// add it to the result as is
				result.append(character);
			}
			character = iterator.next();
		}
		return result.toString();
	}


	/**
	 * http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Programs;action=
	 * display;num=1159828167;start=0#0
	 * 
	 * @param string String
	 * @return String
	 */
	String URLEncode(String string) {
		String output = new String();
		try {
			byte[] input = string.getBytes("UTF-8");
			for (int i = 0; i < input.length; i++) {
				if (input[i] < 0) {
					// output += ('%' + hex(input[i])); // see hex method in
					// processing
				}
				else if (input[i] == 32) {
					output += '+';
				}
				else {
					output += (char) (input[i]);
				}
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return output;
	}


	static public String replace(String theSourceString, String theSearchForString, String theReplaceString) {
		if (theSourceString.length() < 1) {
			return "";
		}
		int p = 0;

		while (p < theSourceString.length() && (p = theSourceString.indexOf(theSearchForString, p)) >= 0) {
			theSourceString = theSourceString.substring(0, p) + theReplaceString + theSourceString.substring(p + theSearchForString.length(), theSourceString.length());
			p += theReplaceString.length();
		}
		return theSourceString;
	}


	/**
	 * convert a hex number into an int
	 * 
	 * @param theHex
	 * @return
	 */
	static public int parseHex(String theHex) {
		int myLen = theHex.length();
		int a, r, b, g;
		switch (myLen) {
		case (8):
			break;
		case (6):
			theHex = "ff" + theHex;
			break;
		default:
			theHex = "ff000000";
			break;
		}
		a = (new Integer(Integer.parseInt(theHex.substring(0, 2), 16))).intValue();
		r = (new Integer(Integer.parseInt(theHex.substring(2, 4), 16))).intValue();
		g = (new Integer(Integer.parseInt(theHex.substring(4, 6), 16))).intValue();
		b = (new Integer(Integer.parseInt(theHex.substring(6, 8), 16))).intValue();
		return (a << 24 | r << 16 | g << 8 | b);
	}


	static public String intToString(int theInt) {
		int a = ((theInt >> 24) & 0xff);
		int r = ((theInt >> 16) & 0xff);
		int g = ((theInt >> 8) & 0xff);
		int b = ((theInt >> 0) & 0xff);
		String sa = ((Integer.toHexString(a)).length() == 1) ? "0" + Integer.toHexString(a) : Integer.toHexString(a);
		String sr = ((Integer.toHexString(r)).length() == 1) ? "0" + Integer.toHexString(r) : Integer.toHexString(r);
		String sg = ((Integer.toHexString(g)).length() == 1) ? "0" + Integer.toHexString(g) : Integer.toHexString(g);
		String sb = ((Integer.toHexString(b)).length() == 1) ? "0" + Integer.toHexString(b) : Integer.toHexString(b);
		return sa + sr + sg + sb;
	}


	/**
	 * @deprecated
	 */
	@Deprecated protected boolean save(ControlP5 theControlP5, String theFilePath) {
		ControlP5.logger().info("Saving ControlP5 settings in XML format has been removed, have a look at controlP5's properties instead.");
		return false;
	}


	/**
	 * * Convenience method for producing a simple textual representation of an array.
	 * 
	 * <P>
	 * The format of the returned <code>String</code> is the same as
	 * <code>AbstractCollection.toString</code>:
	 * <ul>
	 * <li>non-empty array: [blah, blah]
	 * <li>empty array: []
	 * <li>null array: null
	 * </ul>
	 * 
	 * 
	 * <code>aArray</code> is a possibly-null array whose elements are primitives or objects; arrays
	 * of arrays are also valid, in which case <code>aArray</code> is rendered in a nested,
	 * recursive fashion.
	 * 
	 * @author Jerome Lacoste
	 * @author www.javapractices.com
	 */
	static public String arrayToString(Object aArray) {
		if (aArray == null) {
			return fNULL;
		}

		checkObjectIsArray(aArray);

		StringBuilder result = new StringBuilder(fSTART_CHAR);
		int length = Array.getLength(aArray);
		for (int idx = 0; idx < length; ++idx) {
			Object item = Array.get(aArray, idx);
			if (isNonNullArray(item)) {
				// recursive call!
				result.append(arrayToString(item));
			}
			else {
				result.append(item);
			}
			if (!isLastItem(idx, length)) {
				result.append(fSEPARATOR);
			}
		}
		result.append(fEND_CHAR);
		return result.toString();
	}


	// PRIVATE //
	private static final String fSTART_CHAR = "[";

	private static final String fEND_CHAR = "]";

	private static final String fSEPARATOR = ", ";

	private static final String fNULL = "null";


	private static void checkObjectIsArray(Object aArray) {
		if (!aArray.getClass().isArray()) {
			throw new IllegalArgumentException("Object is not an array.");
		}
	}


	private static boolean isNonNullArray(Object aItem) {
		return aItem != null && aItem.getClass().isArray();
	}


	private static boolean isLastItem(int aIdx, int aLength) {
		return (aIdx == aLength - 1);
	}


	protected static String formatGetClass(Class<?> c) {
		if (c == null)
			return null;
		final String pattern = "class ";
		return c.toString().startsWith(pattern) ? c.toString().substring(pattern.length()) : c.toString();
	}


	static public boolean inside(int[] theRect, PVector theVector) {
		return inside(theRect, theVector.x, theVector.y);
	}


	static public boolean inside(int[] theRect, float theX, float theY) {
		if (theRect.length == 4) {
			return (theX > theRect[0] && theX < theRect[2] && theY > theRect[1] && theY < theRect[3]);
		}
		else {
			return false;
		}
	}


	/* Base64 static methods to encode and decode 
	 * bytes into a String and back
	 *
	 * from  
	 * http://examples.oreilly.com/javacrypt/files/oreilly/jonathan/util/
	 * http://oreilly.com/catalog/javacrypt/chapter/ch06.html
	 */

	static public String encodeBase64(byte[] raw) {
		StringBuffer encoded = new StringBuffer();
		for (int i = 0; i < raw.length; i += 3) {
			encoded.append(encodeBlock(raw, i));
		}
		return encoded.toString();
	}


	protected static char[] encodeBlock(byte[] raw, int offset) {
		int block = 0;
		int slack = raw.length - offset - 1;
		int end = (slack >= 2) ? 2 : slack;
		for (int i = 0; i <= end; i++) {
			byte b = raw[offset + i];
			int neuter = (b < 0) ? b + 256 : b;
			block += neuter << (8 * (2 - i));
		}
		char[] base64 = new char[4];
		for (int i = 0; i < 4; i++) {
			int sixbit = (block >>> (6 * (3 - i))) & 0x3f;
			base64[i] = getBase64Char(sixbit);
		}
		if (slack < 1)
			base64[2] = '=';
		if (slack < 2)
			base64[3] = '=';
		return base64;
	}


	static char getBase64Char(int sixBit) {
		if (sixBit >= 0 && sixBit <= 25)
			return (char) ('A' + sixBit);
		if (sixBit >= 26 && sixBit <= 51)
			return (char) ('a' + (sixBit - 26));
		if (sixBit >= 52 && sixBit <= 61)
			return (char) ('0' + (sixBit - 52));
		if (sixBit == 62)
			return '+';
		if (sixBit == 63)
			return '/';
		return '?';
	}


	static public byte[] decodeBase64(String base64) {
		int pad = 0;
		for (int i = base64.length() - 1; base64.charAt(i) == '='; i--)
			pad++;
		int length = base64.length() * 6 / 8 - pad;
		byte[] raw = new byte[length];
		int rawIndex = 0;
		for (int i = 0; i < base64.length(); i += 4) {
			int block = (getBase64Value(base64.charAt(i)) << 18) + (getBase64Value(base64.charAt(i + 1)) << 12) + (getBase64Value(base64.charAt(i + 2)) << 6) + (getBase64Value(base64.charAt(i + 3)));
			for (int j = 0; j < 3 && rawIndex + j < raw.length; j++)
				raw[rawIndex + j] = (byte) ((block >> (8 * (2 - j))) & 0xff);
			rawIndex += 3;
		}
		return raw;
	}


	static int getBase64Value(char c) {
		if (c >= 'A' && c <= 'Z')
			return c - 'A';
		if (c >= 'a' && c <= 'z')
			return c - 'a' + 26;
		if (c >= '0' && c <= '9')
			return c - '0' + 52;
		if (c == '+')
			return 62;
		if (c == '/')
			return 63;
		if (c == '=')
			return 0;
		return -1;
	}


	static public int getBit(int theByte, int theIndex) {
		int bitmask = 1 << theIndex;
		return ((theByte & bitmask) > 0) ? 1 : 0;
	}


	static public byte setHigh(byte theByte, int theIndex) {
		return (byte) (theByte | (1 << theIndex));
	}


	static public byte setLow(byte theByte, int theIndex) {
		return (byte) (theByte & ~(1 << theIndex));
	}


	static public byte[] intToByteArray(int a) {
		byte[] ret = new byte[2];
		ret[1] = (byte) (a & 0xFF);
		ret[0] = (byte) ((a >> 8) & 0xFF);
		//ret[0] = (byte) ((a >> 16) & 0xFF);   
		//ret[0] = (byte) ((a >> 24) & 0xFF);
		return ret;
	}


	static public int byteArrayToInt(byte[] b) {
		int value = 0;
		for (int i = 0; i < 2; i++) {
			int shift = (2 - 1 - i) * 8;
			value += (b[i] & 0x00FF) << shift;
		}
		return value;
	}


	static String join(List<String> list, String delimiter) {
		StringBuilder b = new StringBuilder();
		for (String item : list) {
			b.append(item).append(delimiter);
		}
		return b.toString();
	}
}
